package com.wutong.erb.filter;

import com.wutong.erb.other.CookieBuilder;
import com.wutong.erb.util.DeviceUtil;
import com.wutong.erb.util.JwtUtil;
import io.jsonwebtoken.Claims;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.filter.OncePerRequestFilter;

import java.io.IOException;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * 页面添加token过滤器
 * @author wutong
 */
@Slf4j
@RequiredArgsConstructor
public class PageAddTokenFilter extends OncePerRequestFilter {

    private final RedisTemplate<String, String> redisTemplate;

    @Override
    protected void doFilterInternal(@NotNull HttpServletRequest request,
                                    @NotNull HttpServletResponse response,
                                    @NotNull FilterChain filterChain) throws ServletException, IOException {
        // 对页面添加Token，防止CSRF攻击，方便维持类似会话的流畅操作页面，如登录、注册等
        log.debug("对页面添加Token，防止CSRF攻击，方便维持类似会话的流畅操作页面，如登录、注册等");
        String uri = request.getRequestURI();
        String key = request.getRemoteAddr() + "Token[" + DeviceUtil.generateDeviceHash(request) + "]";
        // 若Redis缓存中存在Token，则验证Redis缓存中的Token，若验证通过，则直接通过验证，否则重新生成Token
        log.debug("若Redis缓存中存在Token，则验证Redis缓存中的Token，若验证通过(并比较设备信息)，则直接通过验证，否则重新生成Token");
        String redisToken = redisTemplate.opsForValue().get(key);
        if (redisToken != null && JwtUtil.validateToken(redisToken)) {
            Claims claims = JwtUtil.parseToken(redisToken);
            if (!(claims != null && claims.get("deviceHash") != null && DeviceUtil.isRequestFromValidDevice(request, claims.get("deviceHash", String.class)))) {
                log.debug("Redis缓存中的Token验证失败，重新生成Token");
                redisTemplate.delete(key);
            } else {
                log.debug("Redis缓存中的Token验证通过");
                filterChain.doFilter(request, response);
                return;
            }
        }
        // 构造Token
        log.debug("构造Token");
        String token = JwtUtil.generateToken(
                uri,
                Map.of(
                        "ip", request.getRemoteAddr(),
                        "deviceHash", DeviceUtil.generateDeviceHash(request),
                        "requestTime", System.currentTimeMillis(),
                        "userAgent", request.getHeader("User-Agent")
                ), 1, TimeUnit.HOURS
        );
        // 添加Token到Cookies中和Header中，并写入Redis缓存
        log.debug("添加Token到Cookies中和Header中，并写入Redis缓存");

        redisTemplate.opsForValue().set(key, token, 1, TimeUnit.HOURS);
        log.debug("添加Token到Cookies中");
        response.addCookie(
                new CookieBuilder("TOKEN", token)
                        .setPath("/")
                        .setDomain(request.getServerName())
                        .setHttpOnly(true)
                        .setMaxAge(3600)
                        .setSecure(false)
                        .build()
        );
        // 通过验证，执行请求
        filterChain.doFilter(request, response);
    }
}
